﻿////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// <copyright>Copyright 2008-2011 Andreas Huber Doenni</copyright>
// Distributed under the GNU General Public License version 2 (GPLv2).
// See accompanying file License.txt or copy at http://phuse.codeplex.com/license.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Phuse.Mime
{
    using System;
    using System.IO;

    using Phuse.IO;

    /// <summary>Provides the implementation for all Parse methods.</summary>
    internal static class ParseHelper
    {
        /// <summary>Creates a new object of type <typeparamref name="TProcessor"/> and repeatedly calls its
        /// <see cref="ILineProcessor{T}.ProcessFirstLine"/> method with a <b>all</b> the lines in
        /// <paramref name="stream"/>.</summary>
        /// <typeparam name="TProcessor">The type of the processor to use.</typeparam>
        /// <typeparam name="TResult">The type of the result that will be returned.</typeparam>
        /// <param name="stream">The stream to read from.</param>
        /// <param name="bufferSize">The size of the temporary in-memory buffer that is allocated to read from
        /// <paramref name="stream"/>.</param>
        /// <returns>The return value of <typeparamref name="TProcessor"/>.<see cref="ILineProcessor{T}.GetResult"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException"><paramref name="stream"/> equals <c>null</c>.</exception>
        /// <exception cref="ArgumentException"><paramref name="stream"/> contains at least one line that is longer than
        /// <paramref name="bufferSize"/>.</exception>
        internal static TResult Parse<TProcessor, TResult>(Stream stream, int bufferSize)
            where TProcessor : ILineProcessor<TResult>, new()
        {
            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }

            if (bufferSize <= 0)
            {
                throw new ArgumentOutOfRangeException("bufferSize", ExceptionMessages.PositiveNumberRequired);
            }

            TProcessor processor = new TProcessor();
            var buffer = new ReadBuffer(stream.Read, bufferSize);
            ArraySegment<byte> segment;

            try
            {
                while ((segment = buffer.GetLine()).Count > 0)
                {
                    buffer.Index = processor.ProcessFirstLine(segment);
                }
            }
            catch (InvalidOperationException)
            {
                throw new ArgumentException(
                    "The stream must not contain a line longer than the current buffer size of " + bufferSize +
                        " bytes.",
                    "stream");
            }

            return processor.GetResult();
        }
    }
}
